import React, { Component, FC } from 'react';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';

export interface RouteComponentPropsPlus<Params extends { [K in keyof Params]?: string | undefined } = {}>
	extends RouteComponentProps<Params> {
	breadcrumbName: string;
	meta: Record<string, any>;
}

export interface RouterConfig {
	path: string;
	component: () => Promise<any>;
	breadcrumbName: string;
	meta?: Record<string, any>;
	children?: Array<RouterConfig>;
}
interface RouterInitState {
	config: Array<RouterConfig>;
	loading: Component | FC | JSX.Element | HTMLElement | null;
	BeforeEnter?: (to: RouteComponentProps, from: any) => Promise<any> | undefined;
	Mounted?: (to: RouteComponentProps, from: any) => Promise<any> | undefined;
	AfterEnter?: (to: RouteComponentProps, from: any) => void;
}

interface RouterBase extends RouterInitState {
	handleConfig: (conf: RouterConfig[]) => RouterConfig[];
	render: () => JSX.Element[];
	createRouterComponent: (chunkFn: () => Promise<any>) => any;
}

interface RouterHandleConfig {
	path: string;
	component: any;
	breadcrumbName: string;
	meta?: Record<string, any>;
	children?: Array<RouterHandleConfig>;
}

interface IState {
	loading: boolean;
	Component: Component<any, any> | FC<any> | any;
}

/**
 * 代码自动按路由分割、资源按需加载
 *
 * @export
 * @class RouterCreate
 * @implements {RouterBase}
 */
export default class RouterCreate implements RouterBase {
	/**
	 * 当前路由信息
	 *
	 * @type {(RouteComponentPropsPlus | undefined)}
	 * @memberof RouterCreate
	 */
	public currentRoute: RouteComponentPropsPlus | undefined = undefined;
	/**
	 * 路由配置，使用import(*)动态导入语法
	 *
	 * @type {RouterConfig[]}
	 * @memberof RouterCreate
	 */
	public config: RouterConfig[] = [];

	public loading: Component | FC | JSX.Element | HTMLElement | null = null;

	/**
	 * 生命周期，加载路由前调用，支持返回Promise
	 *
	 * @memberof RouterCreate
	 */
	public BeforeEnter?: (to: RouteComponentProps) => Promise<any> | undefined;

	/**
	 * 生命周期，加载完成后，进入渲染路由前调用
	 *
	 * @memberof RouterCreate
	 */
	public Mounted?: (to: RouteComponentProps) => Promise<any> | undefined;

	/**
	 * 生命周期，进入路由后回调
	 *
	 * @memberof RouterCreate
	 */
	public AfterEnter?: (to: RouteComponentProps) => void;

	private constructor(config: RouterInitState = { config: [], loading: null }) {
		Object.assign(this, config);
	}

	public setCurrentRoute = (currentRoute: RouteComponentPropsPlus): void => {
		this.currentRoute = currentRoute;
	};

	/**
	 * 创建异步加载组件以及路由生命周期
	 *
	 * @param {() => Promise<any>} chunkFn
	 * @returns {*}
	 * @memberof RouterCreate
	 */
	public createRouterComponent(chunkFn: () => Promise<any>): any {
		const BeforeEnter = this.BeforeEnter;
		const Mounted = this.Mounted;
		const AfterEnter = this.AfterEnter;
		const setCurrentRoute = this.setCurrentRoute;
		const Loading = this.loading;
		return class AsyncImportComponent extends Component<RouteComponentPropsPlus, IState> {
			private constructor(props: RouteComponentPropsPlus) {
				super(props);
				this.state = {
					loading: true,
					Component: Loading,
				};
			}

			componentDidMount(): void {
				this.setState({
					loading: true,
				});
				(async (): Promise<void> => {
					setCurrentRoute(this.props);
					BeforeEnter && (await BeforeEnter(this.props));
					const chunk = await chunkFn();
					Mounted && (await Mounted(this.props));
					this.setState({
						Component: chunk.default,
						loading: false,
					});

					// 测试
					// await new Promise((resolve) => {
					//     setTimeout(() => {
					//         chunkFn().then((chunk: any) => {
					//             this.setState({
					//                 Component: chunk.default,
					//                 loading: false
					//             });
					//         });
					//         resolve();
					//     }, 1000);
					// });
				})();
			}

			componentDidUpdate(): void {
				const { loading } = this.state;
				if (!loading) {
					AfterEnter && AfterEnter(this.props);
				}
			}

			render(): JSX.Element | null {
				const { Component } = this.state;
				const res = Component ? (
					<Component {...this.props} />
				) : // <>
				// 	<div className="fadeIn fullscreen p-a bg-w" />
				// 	<Component {...this.props} />
				// </>
				null;
				return res;
			}
		};
	}

	/**
	 * 处理路由配置
	 *
	 * @param {RouterConfig[]} conf
	 * @returns {RouterHandleConfig[]}
	 * @memberof RouterCreate
	 */
	handleConfig(conf: RouterConfig[]): RouterHandleConfig[] {
		return this.handleConfigTree2List(conf).map((item) => {
			const { component } = item;
			return {
				...item,
				component: this.createRouterComponent(component),
			};
		});
	}

	/**
	 * 处理树形路由结构
	 *
	 * @param {RouterConfig[]} conf
	 * @returns {RouterConfig[]}
	 * @memberof RouterCreate
	 */
	handleConfigTree2List(conf: RouterConfig[]): RouterConfig[] {
		const res: RouterConfig[] = [];
		const quene: RouterConfig[] = [...conf];
		while (quene.length) {
			const item = quene.shift();
			if (item) {
				res.push(item);
				if (item.children) {
					quene.unshift(...item.children);
				}
			}
		}
		return res;
	}

	handleConfigTree(conf: RouterConfig[]): RouterHandleConfig[] {
		const quene: RouterConfig[] = [...conf];
		while (quene.length) {
			const item = quene.shift();
			if (item) {
				item.component = this.createRouterComponent(item.component);
				if (item.children) {
					quene.unshift(...item.children);
				}
			}
		}
		return conf;
	}

	handleConfigTree2RouteTree(conf: RouterHandleConfig[]): JSX.Element[] | null {
		const createRoute = (conf: RouterHandleConfig[]): JSX.Element[] | null => {
			const target: JSX.Element[] = [];
			for (let i = 0; i < conf.length; i++) {
				const item = conf[i];
				if (item) {
					const key = Math.random().toString(36).substring(2);
					if (item.children) {
						const childrenRoute = createRoute(item.children);
						if (item.meta && item.meta.switch) {
							target.push(
								...[
									<Route
										key={key}
										path={item.path}
										{...item.meta}
										// eslint-disable-next-line no-loop-func
										component={(props: any): JSX.Element => (
											<item.component
												{...props}
												meta={{ ...item.meta }}
												breadcrumbName={item.breadcrumbName}
											/>
										)}
									/>,
									...(childrenRoute || []),
								],
							);
						} else {
							target.push(
								<Route
									key={key}
									path={item.path}
									{...item.meta}
									// eslint-disable-next-line no-loop-func
									component={(props: any): JSX.Element => (
										<item.component
											{...props}
											className="fadeIn"
											meta={{ ...item.meta }}
											breadcrumbName={item.breadcrumbName}
										>
											<Switch>{childrenRoute}</Switch>
										</item.component>
									)}
								/>,
							);
						}
					} else {
						target.push(
							<Route
								key={key}
								path={item.path}
								{...item.meta}
								// eslint-disable-next-line no-loop-func
								render={(props: any): JSX.Element => (
									<item.component
										{...props}
										className="fadeIn"
										meta={{ ...item.meta }}
										breadcrumbName={item.breadcrumbName}
									/>
								)}
							/>,
						);
					}
				}
			}
			return target;
		};
		return createRoute(conf);
	}

	/**
	 * 生成路由列表
	 *
	 * @returns {JSX.Element[]}
	 * @memberof RouterCreate
	 */
	public render(): any {
		const configTree = this.handleConfigTree(this.config);
		return this.handleConfigTree2RouteTree(configTree);

		// return this.handleConfig(this.config).map((item, i) => {
		// 	return (
		// 		<Route
		// 			key={i}
		// 			path={item.path}
		// 			{...item.meta}
		// 			render={(props: any): JSX.Element => (
		// 				<item.component {...props} meta={{ ...item.meta }} breadcrumbName={item.breadcrumbName} />
		// 			)}
		// 		/>
		// 	);
		// });
	}
}
